#include <stdlib.h>
#include <typeinfo>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include "ErreurInterpreteur.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnaire::NoeudOperateurUnaire ( Symbole operateur,
                                               Noeud* operande) {
  this->operateur=operateur;
  this->operande=operande;
}

int NoeudOperateurUnaire::getValeur(){
	int valeur=0;
	int op=operande->getValeur();
	if		(this->operateur=="-")
	{
		valeur = -op;
	}
	else if	(this->operateur=="non")
	{
		valeur = !(op);
	}
	return valeur;
}

void NoeudOperateurUnaire::afficher(unsigned short indentation){
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Unaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operande->afficher(indentation+1);  // on affiche le fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if      (this->operateur=="+") valeur = og+od; 
  else if (this->operateur=="-") valeur = og-od;
  else if (this->operateur=="*") valeur = og*od; 
  else if (this->operateur=="/")
	if (od!=0)
		valeur = og/od;
  	else {
		throw ErreurMaths();
  	}
  else if (this->operateur=="et") valeur = og && od;
  else if (this->operateur=="ou") valeur = og || od;
  else if (this->operateur=="==") valeur = og == od;
  else if (this->operateur=="!=") valeur = og != od;
  else if (this->operateur=="<=") valeur = og <= od;
  else if (this->operateur==">=") valeur = og >= od;
  else if (this->operateur=="<") valeur = og < od;
  else if (this->operateur==">") valeur = og > od;
  
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi() : tabCond(), tabSeq(){
}

int NoeudInstSi::getValeur(){
	unsigned int i=0;
	while (i<tabCond.size() && tabCond[i]!=NULL){
		if(tabCond[i]->getValeur()){
			tabSeq[i]->getValeur();  //on évalue la séquence dont la condition est vérifiée
		}
		i++;
	}
	if (i<tabCond.size()){
		tabSeq[i]->getValeur();
	}
	return 0;
}

void NoeudInstSi::afficher(unsigned short indentation){
  Noeud::afficher(indentation);
  cout << "Noeud - Instruction Si de " << tabCond.size() << " condition(s)." << endl;
  unsigned int i=0;
  while (i<tabCond.size() && tabCond[i]!=NULL){
	tabCond[i]->afficher(indentation+1);  // on affiche fils gauche et fils droit
	tabSeq[i]->afficher(indentation+1);   // en augmentant l'indentation
	i++;
  }
  if (i<tabCond.size()){
	  tabSeq[i]->afficher(indentation+1);
  }
}

void NoeudInstSi::ajoute(Noeud* condition, Noeud* sequence) {
  tabCond.push_back(condition);
  tabSeq.push_back(sequence);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////
NoeudInstSelon::NoeudInstSelon(Noeud* variable) : tabCond(), tabSeq(){
	this->variable=variable;
}

int NoeudInstSelon::getValeur(){
	unsigned int i=0;
	while (i<tabCond.size() && tabCond[i]!=NULL){
		if(((SymboleValue*)this->variable)->estDefini()){
			if(((SymboleValue*)this->variable)->getValeur()==tabCond[i]->getValeur()){
				tabSeq[i]->getValeur();  //on évalue la séquence dont la condition est vérifiée
			}
		}
		else{
			throw(ErreurInitialisation());
		}
		i++;
	}
	if (i<tabCond.size()){
		tabSeq[i]->getValeur();
	}
	return 0;
}

void NoeudInstSelon::afficher(unsigned short indentation){
  Noeud::afficher(indentation);
  cout << "Noeud - Instruction Selon de " << tabCond.size() << " cas." << endl;
  unsigned int i=0;
  while (i<tabCond.size() && tabCond[i]!=NULL){
	tabCond[i]->afficher(indentation+1);  // on affiche fils gauche et fils droit
	tabSeq[i]->afficher(indentation+1);   // en augmentant l'indentation
	i++;
  }
  if (i<tabCond.size()){
	  tabSeq[i]->afficher(indentation+1);
  }
}

void NoeudInstSelon::ajoute(Noeud* condition, Noeud* sequence) {
  tabCond.push_back(condition);
  tabSeq.push_back(sequence);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudIter
////////////////////////////////////////////////////////////////////////////////
int NoeudIter::getValeur(){
	while(this->getCond()->getValeur()){
		this->getSeqInst()->getValeur(); //si la condition est vérifiée, évaluation de la séquence
	}
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstTantQue
////////////////////////////////////////////////////////////////////////////////
NoeudInstTq::NoeudInstTq(Noeud* cond, Noeud* seqInst){
	this->setCond(cond);
	this->setSeqInst(seqInst);
}

void NoeudInstTq::afficher(unsigned short indentation){
	Noeud::afficher(indentation);
	cout << "Noeud - Instruction Tant Que" << endl;
	this->getCond()->afficher(indentation+1); // affiche la condition
	this->getSeqInst()->afficher(indentation+1); // affiche la séquence d'instruction
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////
NoeudInstRepeter::NoeudInstRepeter(Noeud* seqInst, Noeud* cond){
	this->setCond(cond);
	this->setSeqInst(seqInst);
}

int NoeudInstRepeter::getValeur(){
	this->getSeqInst()->getValeur(); //évaluation de la séquence obligatoire
	return NoeudIter::getValeur();
}

void NoeudInstRepeter::afficher(unsigned short indentation){
	Noeud::afficher(indentation);
	cout << "Noeud - Instruction Repeter" << endl;
	this->getCond()->afficher(indentation+1); // affiche la condition
	this->getSeqInst()->afficher(indentation+1); // affiche la séquence d'instruction
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////
NoeudInstPour::NoeudInstPour(Noeud* affect, Noeud* cond, Noeud* instr, Noeud* seqInst){
	this->affect=affect;
	this->setCond(cond);
	NoeudSeqInst* si = (NoeudSeqInst*)seqInst;
	si->ajouteInstruction(instr); //l'instruction (d'incrémentation) est ajouté en fin de séquence
	this->setSeqInst(si);
}

int NoeudInstPour::getValeur(){
	this->affect->getValeur(); //évaluation de l'affectation avant de boucler
	return NoeudIter::getValeur();
}

void NoeudInstPour::afficher(unsigned short indentation){
	Noeud::afficher(indentation);
	cout << "Noeud - Instruction Pour" << endl;
	this->affect->afficher(indentation+1); //affiche l'affectation
	this->getCond()->afficher(indentation+1); // affiche la condition
	this->getSeqInst()->afficher(indentation+1); // affiche la séquence d'instruction
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////
NoeudInstLire::NoeudInstLire(Noeud* variable){
	this->variable=variable;
}

int NoeudInstLire::getValeur(){
	if(typeid(((SymboleValue*)this->variable)->getValeur())==typeid(int)){
		int valeur;
		cin >> valeur;
		((SymboleValue*)variable)->setValeur(valeur);
	}
	else{
		char* valeur;
		cin.getline(valeur,256);
//		((SymboleValue*)variable)->setValeur(valeur);
	}
	return 1; //instruction atomique, convention de retour 1
}

void NoeudInstLire::afficher(unsigned short indentation){
	Noeud::afficher(indentation);
	cout << "Noeud - Instruction Lire" << endl;
	this->variable->afficher(indentation+1); //affiche la variable
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////
NoeudInstEcrire::NoeudInstEcrire(Noeud* contenu){
	this->contenu=contenu;
}

int NoeudInstEcrire::getValeur(){
	if (typeid(*(this->contenu))==typeid(NoeudOperateurBinaire) // si c'est une expression, afficher sa valeur
		|| typeid(*(this->contenu))==typeid(NoeudOperateurUnaire)){
		cout << this->contenu->getValeur(); //évaluation du contenu de l'instruction en l'affichant
	}	
	else if (typeid(*(this->contenu))==typeid(SymboleValue)){ // si le pointeur pointe sur un symbole valué
		if (((SymboleValue*)this->contenu)->estDefini()){
			cout << ((SymboleValue*)this->contenu)->getValeur();
		}
		else{
			throw ErreurInitialisation();
		}
	}
	return 1; //instruction atomique, convention de retour 1
}

void NoeudInstEcrire::afficher(unsigned short indentation){
	Noeud::afficher(indentation);
	cout << "Noeud - Instruction Ecrire" << endl;
	this->contenu->afficher(indentation+1); //affiche la variable
}